﻿using Lynn.Infastructure.Utils;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using System.Text.Json.Serialization;

namespace Lynn.Infastructure.Aop.Jwt
{
    public class JwtBuilder
    {
        private JwtOption _jwtOption;
        public JwtBuilder(JwtOption jwtOption)
        {
            this._jwtOption = jwtOption;
        }
        /// <summary>
        /// 创建jwttoken,源码自定义
        /// </summary>
        /// <param name="payLoad"></param>
        /// <param name="header"></param>
        /// <returns></returns>
        public JwtToken CreateToken(Dictionary<string, object> payLoad, Dictionary<string, object> header = null)
        {
            if (header == null)
            {
                header = new Dictionary<string, object>(new List<KeyValuePair<string, object>>() {
                    new KeyValuePair<string, object>("alg", "HS256"),
                    new KeyValuePair<string, object>("typ", "JWT")
                });
            }
            //添加jwt可用时间
            var now = DateTime.Now;
            payLoad["nbf"] = ToUnixEpochDate(now);//可用时间起始
            payLoad["exp"] = ToUnixEpochDate(now.AddSeconds(_jwtOption.ExpireSeconds));//可用时间结束

            var encodedHeader = Base64UrlEncoder.Encode(JsonHelper.ObjectToJson(header));
            var encodedPayload = Base64UrlEncoder.Encode(JsonHelper.ObjectToJson(payLoad));

            var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_jwtOption.Key));
            var encodedSignature = Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(encodedHeader, ".", encodedPayload))));

            var encodedJwt = string.Concat(encodedHeader, ".", encodedPayload, ".", encodedSignature);
            return new JwtToken
            {
                Token = encodedJwt,
                Exp = _jwtOption.ExpireSeconds,
            };
        }
        /// <summary>
        /// 颁发token 调用自身的创建方式
        /// </summary>
        /// <param name="payLoad">额外参数</param>
        /// <param name="expiresMinute">过期时间(分钟)</param>
        /// <returns></returns>
        public JwtToken CreateTokenByHandler(Dictionary<string, object> payLoad)
        {
            var now = DateTime.Now;
            var claims = new List<Claim>();
            foreach (var item in payLoad.Keys)
            {
                if (payLoad[item] == null || payLoad[item]?.ToString() == "") {
                    continue;
                }
                var tempClaim = new Claim(item, payLoad[item]?.ToString());
                claims.Add(tempClaim);
            }
            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOption.Key));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
            var jwt = new JwtSecurityToken(
                issuer: _jwtOption.Issuer,
                audience: _jwtOption.Audience,
                claims: claims,
                notBefore: now,
                expires: now.AddSeconds(_jwtOption.ExpireSeconds),
                signingCredentials: creds);
            var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
            return new JwtToken
            {
                Token = encodedJwt,
                Exp = _jwtOption.ExpireSeconds,
            };
        }
        /// <summary>
        /// 验证身份 验证签名的有效性,
        /// </summary>
        /// <param name="encodeJwt"></param>
        /// <param name="validatePayLoad">自定义各类验证； 是否包含那种申明，或者申明的值， </param>
        /// 例如：payLoad["aud"]?.ToString() == "roberAuddience";
        /// 例如：验证是否过期 等
        /// <returns></returns>
        public bool Validate(string encodeJwt, Func<Dictionary<string, object>, bool> validatePayLoad)
        {
            var success = true;
            var jwtArr = encodeJwt.Split('.');
            var header = JsonHelper.JsonToObject<Dictionary<string, object>>(Base64UrlEncoder.Decode(jwtArr[0]));
            var payLoad = JsonHelper.JsonToObject<Dictionary<string, object>>(Base64UrlEncoder.Decode(jwtArr[1]));

            var hs256 = new HMACSHA256(Encoding.ASCII.GetBytes(_jwtOption.Key));
            //首先验证签名是否正确（必须的）
            success = success && string.Equals(jwtArr[2], Base64UrlEncoder.Encode(hs256.ComputeHash(Encoding.UTF8.GetBytes(string.Concat(jwtArr[0], ".", jwtArr[1])))));
            if (!success)
            {
                return success;//签名不正确直接返回
            }
            //其次验证是否在有效期内（也应该必须）
            var now = ToUnixEpochDate(DateTime.UtcNow);
            success = success && (now >= long.Parse(payLoad["nbf"].ToString()) && now < long.Parse(payLoad["exp"].ToString()));

            //再其次 进行自定义的验证
            success = success && validatePayLoad(payLoad);

            return success;
        }
        /// <summary>
        /// 获取jwt中的payLoad
        /// </summary>
        /// <param name="encodeJwt"></param>
        /// <returns></returns>
        public Dictionary<string, object> GetPayLoad(string encodeJwt)
        {
            var jwtArr = encodeJwt.Split('.');
            var payLoad = JsonHelper.JsonToObject<Dictionary<string, object>>(Base64UrlEncoder.Decode(jwtArr[1]));
            return payLoad;
        }
        public long ToUnixEpochDate(DateTime date) =>
            (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero)).TotalSeconds);
    }
}
